Skip to content

fix: GAP-003 — refuse to silently fall back to Ollama when a credentialed provider's module is missing - #263

Draft
Brian Krabach (bkrabach) wants to merge 1 commit into
mainfrom
fix/gap-003-provider-env-detection
Draft

fix: GAP-003 — refuse to silently fall back to Ollama when a credentialed provider's module is missing#263
Brian Krabach (bkrabach) wants to merge 1 commit into
mainfrom
fix/gap-003-provider-env-detection

Conversation

@bkrabach

Copy link
Copy Markdown
Collaborator

Summary

This is a cross-platform default-behaviour change, not a Windows fix — despite being extracted from a PR (#259) that filed it under "Windows compatibility gaps." It changes how detect_provider_from_env() / auto_init_from_env() behave for every Linux, macOS, WSL, and Windows user who hits the non-interactive auto-configure path (used whenever stdin isn't a TTY: CI, Docker, shadow/headless environments).

Old behaviour

detect_provider_from_env() treated two very different situations identically — both simply continued past the provider in the priority loop:

  1. "No credentials set for this provider" (nothing to report, correct to move on)
  2. "Credentials ARE present, but this provider's module isn't installed/importable" (a real, actionable problem)

Concretely: a user with a valid ANTHROPIC_API_KEY set, but whose provider-anthropic module wasn't installed (or failed to install), silently fell through the loop to the credential-free Ollama fallback. That choice was then persisted to settings.yaml — not a one-time mistake, every subsequent run kept using Ollama. The only symptom the user ever saw was a ConnectionError against a local Ollama server they never set up or wanted, with nothing telling them their real API key was ever seen and discarded.

New behaviour

If a provider has all of its required credential env vars present but its module is not installed, that's recorded and blocks the Ollama fallback. If no other candidate provider is both credentialed and installed, detect_provider_from_env() raises CredentialedProviderModuleMissingError, naming the provider, the env vars found, and the fix (amplifier provider install <name>). auto_init_from_env() catches this specifically and prints a loud, specific error instead of quietly "succeeding" onto the wrong backend. Nothing is persisted, so the very next run — once the module is installed — gets a real, working second chance.

Unaffected (verified by tests, not just asserted):

  • Genuine no-cloud-credentials case still lands on Ollama quietly, exactly as before.
  • A higher-priority provider with a missing module no longer blocks a lower-priority provider that IS both credentialed and installed — that one is still selected.

Who's affected, and under what conditions

Every platform — this code path has no OS gating at all. Triggered specifically when:

  • The CLI runs non-interactively (stdin is not a TTY — CI pipelines, Docker containers, headless/shadow environments), and
  • A provider's credential env var(s) are present in the environment, and
  • That provider's module is not installed or fails to import.

Before this change, that combination silently reconfigured the user onto Ollama and wrote it to disk. That is a real behavioural change for anyone currently relying on (or unknowingly triggering) the old silent-fallback path — which is exactly why this needs to be reviewed as its own change, not folded into a Windows-labelled bundle where a reviewer would reasonably skim past it as platform-specific.

Why fail loud instead of silently substituting a different provider

The user made an explicit choice by setting a specific provider's credentials in their environment. Silently overriding that choice with Ollama isn't graceful degradation — it's a correctness bug wearing a nice costume. It changes which backend runs, which model actually answers, and for anyone who assumed their cloud key was in effect, it can send prompts to a completely different place than intended, with cost/data implications they never agreed to. An explicit, actionable error that names the exact provider, the exact env vars found, and the exact fix command is strictly better than a misleading downstream ConnectionError that gives no indication what actually happened.

Test evidence

tests/test_provider_env_detect.py — 7 tests exercising detect_provider_from_env() directly (patching only entry_points, not mocking the function itself), covering every branch:

  • No credentials, no installed providers → None
  • No credentials, Ollama installed → quiet Ollama fallback (regression guard for the unaffected case)
  • Credentials present, module installed → that provider selected
  • Credentials present, module missing, Ollama available → raises CredentialedProviderModuleMissingError (not "Ollama")
  • Credentials present, module missing, Ollama also not installed → still raises (previously would've returned None silently)
  • Higher-priority provider's module missing, lower-priority provider both credentialed and installed → lower-priority one is still selected
  • Decisive regression guard: asserts the function must never return "provider-ollama" once a credentialed-but-missing provider was seen

Revert-proof: reverting just the detect_provider_from_env() logic to the old "treat missing module same as missing credentials" behavior (while leaving the exception class defined so imports still resolve) makes exactly the 3 tests targeting the new behavior fail:

FAILED tests/test_provider_env_detect.py::TestDetectProviderFromEnvCredentialedButModuleMissing::test_raises_instead_of_falling_back_to_ollama
FAILED tests/test_provider_env_detect.py::TestDetectProviderFromEnvCredentialedButModuleMissing::test_raises_even_when_ollama_not_installed_either
FAILED tests/test_provider_env_detect.py::TestDetectProviderFromEnvCredentialedButModuleMissing::test_does_not_reach_ollama_when_credentialed_provider_missing
3 failed, 4 passed

confirming these tests actually exercise the new code path, not just something that happened to pass either way.

Full suite: 1308 passed, 1 skipped, 13 deselected, 1 xfailed (this branch carries only this fix + its tests, so the count is smaller than main's ~1316 — expected and fine; what matters is zero failures).

Lint: ruff check clean on all three changed files.

Files changed

  • amplifier_app_cli/provider_env_detect.py — the actual GAP-003 fix: new CredentialedProviderModuleMissingError, updated detect_provider_from_env() logic.
  • amplifier_app_cli/commands/init.py — minimal: import the new exception, and one except CredentialedProviderModuleMissingError handler in auto_init_from_env() so the failure is reported clearly rather than being swallowed by the generic except Exception fallback below it.
  • tests/test_provider_env_detect.py — new regression suite (above).

Provenance

Extracted from #259 (fix/gap-003-020-023-027-021), which bundles this fix together with four unrelated fixes (GAP-020 first-run prompt retry bound, GAP-023/GAP-027 SIGINT handling, GAP-021 history race) across 15 commits and 19 files, all under a single "five Windows compatibility gaps" label. This one isn't Windows-gated and changes default behaviour everywhere, so it's being pulled out for review on its own terms. #259 itself has been left untouched; removing the duplicated commits from it is a separate follow-up.

…aled provider's module is missing

Cross-platform behaviour change (not Windows-specific). Affects every
Linux/macOS/WSL/Windows user of `detect_provider_from_env()` /
`auto_init_from_env()` (the non-interactive auto-configure path used
when stdin is not a TTY: CI, Docker, shadow environments).

Old behaviour: `detect_provider_from_env()` treated "this provider's
module isn't installed" identically to "no credentials set for this
provider" -- both cases just `continue`d past the provider in the
priority loop. A user with a valid `ANTHROPIC_API_KEY` set, but whose
`provider-anthropic` module was not installed (or failed to install),
silently fell through to the credential-free Ollama fallback. That
choice got persisted to settings.yaml, so it wasn't even a one-time
mistake -- every subsequent run kept using Ollama, with no error and
no mention that a real API key was ever seen and discarded. The
symptom the user actually saw was a `ConnectionError` against a local
Ollama server they never set up, which is a much harder thing to
debug than "you're missing a package."

New behaviour: if a provider has all of its required credential env
vars present but its module is not installed/importable, that is
recorded and blocks the Ollama fallback. If no other candidate
provider is both credentialed and installed, `detect_provider_from_env()`
raises `CredentialedProviderModuleMissingError` naming the provider,
the env vars that were found, and the fix (`amplifier provider install
<name>`). `auto_init_from_env()` catches this specifically and prints
a loud, specific error instead of quietly "succeeding" onto the wrong
backend. Nothing is persisted, so the next run gets a real second
chance once the module is installed.

Unaffected: the genuine no-cloud-credentials case still lands on
Ollama quietly, exactly as before (covered by
`test_no_credentials_falls_through_to_ollama`). Unaffected: a
higher-priority provider with a missing module no longer blocks a
lower-priority provider that IS both credentialed and installed --
that one is still selected (`test_falls_through_to_second_credentialed_installed_provider`).

Why fail loud instead of silently substituting a different provider:
the user made an explicit choice by setting a specific provider's
credentials. Silently overriding that choice with Ollama is a
correctness bug dressed up as graceful degradation -- it changes which
backend runs, which model answers, and (for anyone who assumed their
cloud key was in effect) can send prompts to the wrong place entirely.
An explicit, actionable error that names the exact fix is strictly
better than a misleading downstream connection failure.

Test evidence: 7 new tests in tests/test_provider_env_detect.py
exercise detect_provider_from_env() directly (entry_points patched,
not the whole function mocked away) across every branch: no
credentials/no installed providers -> None; no credentials + Ollama
installed -> quiet Ollama fallback (unaffected case, regression
guard); credentials + module installed -> that provider; credentials
+ module missing (with and without Ollama available) -> raises;
higher-priority module missing but lower-priority both credentialed
and installed -> lower-priority one still selected; decisive
regression guard asserting the function must never return
"provider-ollama" once a credentialed-but-missing provider was seen.

Reverting just the detect_provider_from_env() logic back to the old
"treat missing module same as missing credentials" behavior (keeping
the exception class defined so imports still resolve) makes exactly
the 3 tests targeting the new behavior fail with "DID NOT RAISE" /
"provider-ollama" == "provider-ollama", confirming they exercise the
new code path and not just the pre-existing one.

Full suite: 1308 passed, 1 skipped, 13 deselected, 1 xfailed (this
branch only carries GAP-003 + tests, so the count is smaller than
main's ~1316 -- expected). ruff clean on all three changed files.

Extracted from #259, which bundles this
GAP-003 fix together with four unrelated fixes (GAP-020/023/027/021)
across 15 commits and 19 files under a "Windows compatibility gaps"
label. This change is not Windows-gated and needs review on its own
terms as a default-behaviour change for every platform.

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants